﻿using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using System;
using Library.Util;
namespace Library.Winform.Draw {
	public class PolygonDraw : LineDraw {

		public PolygonDraw() {
			Init();
		}
		private void Init() {
			Points = new List<Point>();
			Handles = new List<ushort>();
			_maxPosition = Point.Empty;
			_minPosition.X = Int32.MaxValue;
			_minPosition.Y = Int32.MaxValue;
		}

		private Point _minPosition;
		private Point _maxPosition;
		protected new List<ushort> Handles { private set; get; }
		public PolygonDraw(int x1, int y1, int x2, int y2)
			: this() {
			AddPointCore(new Point(x1, y1));
			AddPoint(x2, y2);
		}
		private void OnNewPointAdded(Point point) {
			if (point.X > _maxPosition.X) _maxPosition.X = point.X;
			if (point.Y > _maxPosition.Y) _maxPosition.Y = point.Y;
			if (point.X < _minPosition.X) _minPosition.X = point.X;
			if (point.Y < _minPosition.Y) _minPosition.Y = point.Y;
		}
		protected List<Point> Points { get; private set; }
		/// <summary>
		/// add new point without check max position change
		/// </summary>
		/// <param name="point"></param>
		/// <returns></returns>
		protected void AddPointCore(Point point) {
			if (Points.Count == 0) StartPoint = point;
			Points.Add(point);
			var index = (ushort)(Handles.Count + 1);
			Handles.Add(index);
		}

		public virtual void AddPoint(Point point) {
			for (int i = Points.Count - 1; i >= 0; i--) {
				var p = Points[i];
				if (p.X == point.X && p.Y == point.Y) return;
			}
			AddPointCore(point);
			OnNewPointAdded(point);
		}

		public void AddPoint(int x, int y) {
			AddPoint(new Point(x, y));
		}

		protected override void DrawMe(Graphics g) {
			if (Points.Count > 1) {
				Point from = Points[0];
				Pen shadowPen = ShadowLen > 0 ? new Pen(ShadowColor, ShadowLen) : null;
				using (var penLine = new Pen(PenColor, PenWidth)) {
					for (int i = 1; i < Points.Count; i++) {
						var to = Points[i];
						DrawLine(g, penLine, shadowPen, ShadowLen, from.X - ScrollOffset.X, from.Y - ScrollOffset.Y, to.X - ScrollOffset.X, to.Y - ScrollOffset.Y);
						from = to;
					}
				}
				if (shadowPen != null)
					shadowPen.Dispose();
			}
		}

		public int PointCount {
			get { return Points.Count; }
		}

		public override ushort[] HandlePoints {
			get { return Handles.ToArray(); }
		}

		public override Point GetPointFromHandle(ushort pointHandler) {
			if (pointHandler > 0 && pointHandler <= Handles.Count) {
				return Points[pointHandler - 1];
			}

			return base.GetPointFromHandle(pointHandler);
		}

		public override Point MaxPosition {
			get { return _maxPosition; }
		}

		public Point MinxPosition {
			get { return _minPosition; }
		}

		protected Point StartPoint;

		protected override Point GetLocation() {
			return StartPoint;
		}

		public override Cursor GetHandleCursor(ushort handleNumber) {
			return Cursors.Default;
		}
		protected override void MoveHandleTo(ushort pointHandler, Point newPoint) {
			if (pointHandler > 0 && pointHandler <= Handles.Count) {
				Points[pointHandler - 1] = newPoint;
				OnNewPointAdded(newPoint);
				DisposeHitGraphic();
			}
		}
		public override void Move(int deltaX, int deltaY) {
			int n = Points.Count;
			for (int i = 0; i < n; i++) {
				var copyPoint = Points[i];
				Points[i] = new Point(copyPoint.X + deltaX, copyPoint.Y + deltaY);
			}
			var newMaxPoint = new Point(_maxPosition.X + deltaX, _maxPosition.Y + deltaY);
			OnNewPointAdded(newMaxPoint);
			DisposeHitGraphic();
		}
		protected override void InitHitGrapphic() {
			if (AreaPath != null)
				return;

			// Create closed path which contains all polygon vertexes
			AreaPath = new GraphicsPath();

			int x1 = 0, y1 = 0;     // previous point

			var enumerator = Points.GetEnumerator();

			if (enumerator.MoveNext()) {
				x1 = enumerator.Current.X;
				y1 = enumerator.Current.Y;
			}

			while (enumerator.MoveNext()) {
				int x2 = enumerator.Current.X;             // current point
				int y2 = enumerator.Current.Y;             // current point

				AreaPath.AddLine(x1, y1, x2, y2);

				x1 = x2;
				y1 = y2;
			}

			AreaPath.CloseFigure();

			// Create region from the path
			AreaRegion = new Region(AreaPath);
		}
		public override void ReadFrom(System.IO.Stream stream) {
			Points.Clear();
			_maxPosition = Point.Empty;
			_minPosition.X = Int32.MaxValue;
			_minPosition.Y = Int32.MaxValue;
			UInt16 count;
			stream.Read(out count);
			for (int i = 0; i < count; i++) {
				int x, y;
				stream.Read(out x);
				stream.Read(out y);
				AddPoint(x, y);
			}
			base.ReadFrom(stream);
		}
		public override object Clone() {
			var clone = new PolygonDraw();
			_maxPosition = Point.Empty;
			_minPosition.X = Int32.MaxValue;
			_minPosition.Y = Int32.MaxValue;
			FillOriginalTo(clone);
			return clone;
		}

		protected void FillOriginalTo(PolygonDraw polygonDraw) {
			foreach (var point in Points) {
				polygonDraw.AddPoint(point);
			}

			base.FillOriginalTo(polygonDraw);

		}


		public override void WriteTo(System.IO.Stream stream) {
			stream.Write((UInt16)Points.Count);
			foreach (var point in Points) {
				stream.Write(point.X);
				stream.Write(point.Y);
			}
			base.WriteTo(stream);
		}

	}
}
